Skip to content

[IMP] website_livechat_external: init module#387

Open
augusto-weiss wants to merge 1 commit intoingadhoc:18.0from
adhoc-dev:18.0-t-65509-awe
Open

[IMP] website_livechat_external: init module#387
augusto-weiss wants to merge 1 commit intoingadhoc:18.0from
adhoc-dev:18.0-t-65509-awe

Conversation

@augusto-weiss
Copy link
Copy Markdown
Contributor

No description provided.

@roboadhoc
Copy link
Copy Markdown
Contributor

Pull request status dashboard

Copilot AI review requested due to automatic review settings April 8, 2026 14:27
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Inicializa el módulo website_livechat_external para incrustar un LiveChat provisto por otra instancia de Odoo (p. ej. Odoo 19) dentro de Odoo 18, aislándolo en un iframe y agregando un acceso rápido en el backend.

Changes:

  • Agrega vistas de configuración (res.config.settings) para habilitar el livechat externo y definir URL del proveedor + ID de canal.
  • Inyecta un iframe en el layout del website con lógica JS para alternar pointer-events y permitir interacción solo sobre el widget.
  • Incorpora un servicio + ítem de systray en backend para mostrar/abrir el chat.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
website_livechat_external/manifest.py Declara el nuevo módulo, sus datos y assets backend.
website_livechat_external/init.py Inicializa imports de controllers y models.
website_livechat_external/controllers/init.py Exporta el controlador del módulo.
website_livechat_external/controllers/main.py Expone endpoints para servir el frame y configuración del backend.
website_livechat_external/models/init.py Exporta la extensión de res.config.settings.
website_livechat_external/models/res_config_settings.py Define parámetros de sistema para habilitación/URL/canal.
website_livechat_external/views/res_config_settings_views.xml Agrega sección de configuración “LiveChat Externo” en Website.
website_livechat_external/views/website_templates.xml Hereda website.layout para inyectar iframe + JS en el website.
website_livechat_external/static/src/js/external_livechat_service.js Servicio backend que crea el iframe y expone openChat().
website_livechat_external/static/src/js/external_livechat_systray.js Componente systray para disparar openChat().
website_livechat_external/static/src/xml/external_livechat_systray.xml Template OWL del botón de systray.

id="external_livechat_frame"
name="External LiveChat Frame"
inherit_id="website.layout"
active="False"
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

El template hereda website.layout pero está cargado con active="False". Así la vista queda inactiva en BD y no se va a inyectar el iframe aunque el parámetro website_livechat_external.enabled esté en True. Si la activación se controla por ir.config_parameter, conviene dejar la vista activa y que la condición sea solo el t-if (o implementar lógica explícita que active/desactive la vista).

Suggested change
active="False"

Copilot uses AI. Check for mistakes.
Comment on lines +58 to +82
loader_src = f"{provider_url}/im_livechat/loader/{channel_id}"
embed_src = f"{provider_url}/im_livechat/assets_embed.js"

html = f"""<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<style>
html, body {{
margin: 0;
padding: 0;
background: transparent;
overflow: hidden;
}}
</style>
</head>
<body>
<!--
Esta página corre en su propio contexto JS (sin los globals de Odoo 18),
por lo que los scripts de Odoo 19 no colisionan con los de Odoo 18.
-->
<script defer="defer" type="text/javascript" src="{loader_src}"></script>
<script defer="defer" type="text/javascript" src="{embed_src}"></script>
<script type="text/javascript">
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/frame sirve un HTML de mismo origen que ejecuta JS remoto ({provider_url}/im_livechat/...). Aunque el script venga de otro dominio, se ejecuta con el origen de esta instancia y por lo tanto tiene acceso total a cookies/sesión y endpoints (especialmente grave en /backend_frame con sesión del usuario). Si el proveedor no es 100% confiable, esto es una escalada de confianza importante. Alternativas: aislar en otro origen/subdominio (y comunicarte por postMessage), o al menos restringir/whitelistear dominios permitidos (y documentar explícitamente el riesgo) y evitar habilitarlo en backend si no es necesario.

Copilot uses AI. Check for mistakes.
Comment on lines +83 to +92
// Escucha mensajes del parent para abrir/cerrar el livechat.
// El botón vive dentro de un shadow DOM (.o-livechat-root) y no es
// accesible con querySelector desde el parent frame.
window.addEventListener("message", function (ev) {{
if (!ev.data || ev.data.type !== "toggle_livechat") return;
var root = document.querySelector(".o-livechat-root");
if (!root || !root.shadowRoot) return;
var btn = root.shadowRoot.querySelector(".o-livechat-LivechatButton");
if (btn) btn.click();
}});
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

En el listener window.addEventListener("message", ...) del HTML embebido no se valida ev.origin ni ev.source. Aunque X-Frame-Options: SAMEORIGIN reduce el riesgo de embedding cross-origin, sigue siendo recomendable validar que el mensaje proviene del window.parent y del origen esperado antes de ejecutar btn.click() (defensa en profundidad).

Copilot uses AI. Check for mistakes.
Comment on lines +107 to +112
document.addEventListener('mousemove', function (e) {
if (!iframeDoc) { return; }
frame.style.pointerEvents = isOverLivechat(e.clientX, e.clientY)
? 'auto'
: 'none';
});
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Se está ejecutando elementFromPoint() en cada mousemove del documento. En páginas con mucho movimiento de mouse esto puede generar overhead notable (y además se duplica lógica similar en backend). Conviene throttle/debounce (p. ej. con requestAnimationFrame o un timer) para limitar la frecuencia de cómputo y cambios de pointer-events.

Copilot uses AI. Check for mistakes.
Comment on lines +78 to +85
document.addEventListener("mousemove", (e) => {
if (!iframeDoc) {
return;
}
frame.style.pointerEvents = isOverLivechat(e.clientX, e.clientY)
? "auto"
: "none";
});
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Idem al website: document.addEventListener("mousemove", ...) + elementFromPoint() en cada evento puede impactar performance del backend (web client) y correr todo el tiempo aunque el chat no se use. Sugiero throttlear el handler (p. ej. requestAnimationFrame) y, si es posible, activarlo solo cuando el iframe ya cargó y/o cuando el chat esté disponible/abierto.

Copilot uses AI. Check for mistakes.
Comment on lines +11 to +13
setup() {
this.livechat = useState(useService("external_livechat_backend"));
}
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

En el resto del repo los componentes suelen consumir servicios con useService(...) directamente (p. ej. mail_ux/static/src/core/common/composer.js:13-14) sin envolverlos en useState. Aquí useState(useService(...)) puede ser innecesario y dificulta entender si la reactividad depende del servicio o del wrapper. Considerá usar this.livechat = useService("external_livechat_backend") y, si hace falta reactividad, exponer un estado reactivo desde el propio servicio.

Copilot uses AI. Check for mistakes.
Comment on lines +24 to +56
@http.route(
"/website_livechat_external/frame",
type="http",
auth="public",
website=True,
sitemap=False,
)
def livechat_frame(self):
"""
Devuelve una página HTML mínima y aislada que carga los scripts del
livechat del proveedor externo (Odoo 19). Al servirse desde el mismo
origen de Odoo 18, el iframe que la contiene puede manipular su DOM
para habilitar pointer-events sólo sobre el widget del chat.
"""
ICP = request.env["ir.config_parameter"].sudo()

enabled = ICP.get_param("website_livechat_external.enabled", "False")
if enabled not in ("True", "1", "true"):
return request.not_found()

provider_url = ICP.get_param("website_livechat_external.provider_url", "").rstrip("/")
channel_id_raw = ICP.get_param("website_livechat_external.channel_id", "0")

# Validaciones de seguridad básicas
if not provider_url or not _VALID_ORIGIN_RE.match(provider_url):
return request.make_response("URL del proveedor inválida.", status=400)

try:
channel_id = int(channel_id_raw)
if channel_id <= 0:
raise ValueError
except (ValueError, TypeError):
return request.make_response("ID de canal inválido.", status=400)
Copy link

Copilot AI Apr 8, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Este módulo introduce rutas nuevas con validaciones (enabled/provider_url/channel_id) y distintos status codes (404/400/200). En el repo ya hay tests Odoo (base_bg/tests/test_bg_job.py), pero acá no se agregan. Sería bueno sumar al menos tests de controlador para: (1) /frame devuelve 404 si está deshabilitado, (2) 400 si la URL o el channel_id son inválidos, (3) 200 y contiene los <script src=...> esperados cuando la config es válida.

Copilot generated this review using guidance from repository custom instructions.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants